package nl.cypherpunk.statefuzzer;

import net.automatalib.words.Word;

import java.io.*;
import java.util.*;

public class Common {
    public void Common(){};
    public static String byteToHex(byte[] bytes){
        String strHex = "";
        StringBuilder sb = new StringBuilder("");
        for (int n = 0; n < bytes.length; n++) {
            strHex = Integer.toHexString(bytes[n] & 0xFF);
            sb.append((strHex.length() == 1) ? "0" + strHex : strHex);
        }
        return sb.toString().trim();
    }

    public static Integer getRandomState(ArrayList<Double> map) {
        NavigableMap<Double, Integer> hashMap = new TreeMap<>();
        double total = 0;
        for (int i = 0 ; i < map.size(); i++ ) {
            total += map.get(i);
            hashMap.put(total,i);
        }
        Random random =new Random();
        Double weight = 1.0*random.nextInt((int)total);
        return hashMap.ceilingEntry(weight).getValue();
    }

    //list deepCopy
    public static <T> List<T> deepCopy(List<T> src)
            throws IOException, ClassNotFoundException {
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(byteOut);
        out.writeObject(src);
        ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
        ObjectInputStream in = new ObjectInputStream(byteIn);
        return (List<T>) in.readObject();
    }


    /**
     * int to byte[] high to low
     *
     * @param i
     * @return byte[]
     */
    public static byte[] intToByteArray(int i) {
        byte[] result = new byte[4];
        result[3] = (byte) ((i >> 24) & 0xFF);
        result[2] = (byte) ((i >> 16) & 0xFF);
        result[1] = (byte) ((i >> 8) & 0xFF);
        result[0] = (byte) (i & 0xFF);
        return result;
    }

    /**
     * byte[] to int
     * @param bytes
     * @return int
     */
    public static int byteArrayToInt(byte[] bytes) {
        int value=0;
        for(int i = 0; i < 4; i++) {
            int shift= i * 8;
            value +=(bytes[i] & 0xFF) << shift;
        }
        return value;
    }

    public static Integer getRandomSeed(ArrayList<Double> map){
        NavigableMap<Double, Integer> hashMap = new TreeMap<>();
        double total = 0;
        for (int i = 0; i < map.size(); i++) {
            total += map.get(i);
            hashMap.put(total, i);
        }
        Random random = new Random();
        Double weight = 1.0 * random.nextInt((int) total);
        return hashMap.ceilingEntry(weight).getValue();
    }

    public static ArrayList<List<byte[]>> readFromFile(String dir)
    {
        List<String> files = new ArrayList<String>();
        File file = new File(dir);
        if(file.length()==0)
            return null;
        ArrayList<List<byte[]>> seedPool = new ArrayList<>();
        File[] tempList = file.listFiles();
        List fileList = Arrays.asList(tempList);
        Collections.sort(fileList, new Comparator<File>() {
            @Override
            public int compare(File o1, File o2) {
                if (o1.isDirectory() && o2.isFile())
                    return -1;
                if (o1.isFile() && o2.isDirectory())
                    return 1;
                return o1.getName().compareTo(o2.getName());
            }
        });
        for (int i = 0; i < tempList.length; i++) {
            if (tempList[i].isFile()) {
                files.add(tempList[i].toString());
                File temp = new File(tempList[i].toString());
                try {
                    InputStream fileIn = new FileInputStream(temp);
                    //DataInputStream in = new DataInputStream(fileIn);

                    // 使用缓存区读入对象效率更快
                    BufferedInputStream in = new BufferedInputStream(fileIn);
                    byte[] len = new byte[1024] ;
                    int length;
                    List<byte[]> seed = new ArrayList<>();
                    Word<String> inputList = Word.epsilon();
                    while(in.read(len,0,4)!=-1)
                    {
                        length = byteArrayToInt(len);
                        byte[] bys = new byte[length];
                        in.read(bys,0,length);
                        seed.add(bys);
                    }
                    in.close();
                    fileIn.close();
                    seedPool.add(seed);
                }
                catch(Exception e)
                {
                    e.printStackTrace();
                }
            }
        }
        return seedPool;
    }

    public static boolean splice(byte[] current,byte[] target, Random rand)
    {
        int f_loc = -1;
        int l_loc = -1;
        int pos;
        int len = Math.min(current.length,target.length);
        for(pos = 0; pos < len; pos++)
        {
            if(current[pos]==target[pos])
            {
                if(f_loc==-1)
                    f_loc = pos;
                l_loc = pos;
            }
        }
        if(f_loc < 0 || l_loc < 2|| f_loc==l_loc)
            return false;
        int split_at = f_loc + rand.nextInt(l_loc-f_loc);
        System.arraycopy(current,0,target,0,split_at);
        return true;
    }

    public static void messageSave(byte[] seed, String filename) {
        BufferedOutputStream bos = null;
        FileOutputStream fos = null;
        File file = null;
        try {
            file = new File(filename);
            if (file.exists()) {
                return;
            }
            fos = new FileOutputStream(file);
            bos = new BufferedOutputStream(fos);
            bos.write(seed, 0, seed.length);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

    public static void seedSave(List<byte[]> seed, String filename) {
        BufferedOutputStream bos = null;
        FileOutputStream fos = null;
        File file = null;
        try {
            file = new File(filename);
            if (file.exists()) {
                return;
            }
            fos = new FileOutputStream(file);
            bos = new BufferedOutputStream(fos);
            for (int i = 0; i < seed.size(); i++) {
                byte[] tmp = intToByteArray(seed.get(i).length);
                bos.write(tmp, 0, 4);
                bos.write(seed.get(i), 0, seed.get(i).length);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }
}
